/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_kit/zhiqim_git.htm
 *
 * ZhiqimGit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.git.objer.model;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.zhiqim.git.GitConstants;
import org.zhiqim.git.objer.objectid.ObjectId;

import org.zhiqim.kernel.util.mutables.MInt;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Bytes;

/**
 * 提交信息模型对象
 * 
 * 格式如下：
 * tree b11548de78dd3c90bc3bf7dba30c4062dc28a584
 * parent ae2282f08a8a36c463d351d8b152868c2631b54d
 * author 森中灵 <senyeah@qq.com> 1490791737 +0800
 * committer 森中灵 <senyeah@qq.com> 1490791737 +0800
 * 
 * 一二三四五
 * 六七八九十
 *
 * @version v1.0.0 @author zhichenggang 2017-4-16 新建与整理
 */
public class Commit implements GitConstants
{
    private ObjectId tree;
    private List<ObjectId> parentList;
    private PersonIdent author;
    private PersonIdent committer;
    private Charset encoding;
    private String message;

    public Commit()
    {
        this.parentList = new ArrayList<>(1);
    }

    /******************************************************************************/
    //提交六个参数设置
    /******************************************************************************/

    public Commit setTree(ObjectId tree)
    {
        this.tree = tree;
        return this;
    }

    public Commit setParentIds(List<ObjectId> parentList)
    {
        this.parentList = parentList;
        return this;
    }
    
    public Commit setAuthor(PersonIdent author)
    {
        this.author = author;
        return this;
    }
    
    public Commit setCommitter(PersonIdent committer)
    {
        this.committer = committer;
        return this;
    }
    
    public Commit setEncoding(Charset encoding)
    {
        this.encoding = encoding;
        return this;
    }
    
    public Commit setMessage(String message)
    {
        this.message = message;
        return this;
    }
    
    /******************************************************************************/
    //提交六个参数获取
    /******************************************************************************/
    
    public ObjectId getTree()
    {
        return tree;
    }

    public List<ObjectId> getParentList()
    {
        return parentList;
    }
    
    public PersonIdent getAuthor()
    {
        return author;
    }

    public PersonIdent getCommitter()
    {
        return committer;
    }

    public Charset getEncoding()
    {
        return encoding;
    }

    public String getMessage()
    {
        return message;
    }
    
    /******************************************************************************/
    //解析&生成
    /******************************************************************************/
    
    /** 解析 */
    public Commit parse(byte[] content)
    {
        MInt off = new MInt();
        
        //1.tree
        String line = Bytes.BU.getString(content, off, _LF_);
        Asserts.assertStartsWith(line, "tree ", "解析提交信息时，格式错误，没有tree标识");
        tree = ObjectId.parse(line.substring(P_TREE.length));
        
        //2.parent
        while (true)
        {
            line = Bytes.BU.getString(content, off, _LF_);
            Asserts.assertNotNull(line, "解析标签时，格式错误");
            if (!line.startsWith("parent "))
                break;
            
            parentList.add(ObjectId.parse(line.substring(P_PARENT.length)));
        }
            
        //3.author
        Asserts.assertStartsWith(line, "author ", "解析提交信息时，格式错误，没有author标识");
        author = new PersonIdent(line.substring(P_AUTHOR.length));
        
        //4.committer
        line = Bytes.BU.getString(content, off, _LF_);
        Asserts.assertStartsWith(line, "committer ", "解析提交信息时，格式错误，没有committer标识");
        committer = new PersonIdent(line.substring(P_COMMITTER.length));
        
        //5.encoding
        line = Bytes.BU.getString(content, off, _LF_);
        if (line.startsWith("encoding "))
        {//如果有编码则读出并跳过一个换行符
            encoding = Charset.forName(line.substring(P_ENCODING.length));
            off.skip(1);
        }
        else
        {//没有编码，置为默认
            encoding = _UTF_8_C_;
        }
        
        message = Bytes.BU.getString(content, off);
        return this;
    }
    
    /** 生成 */
    public byte[] build() throws IOException
    {
        final ByteArrayOutputStream os = new ByteArrayOutputStream();
        if (encoding == null)
            encoding = _UTF_8_C_;
        final OutputStreamWriter w = new OutputStreamWriter(os, encoding);

        os.write(P_TREE);
        tree.copyTo(os);
        os.write('\n');

        for (ObjectId parent : parentList)
        {
            os.write(P_PARENT);
            parent.copyTo(os);
            os.write('\n');
        }

        os.write(P_AUTHOR);
        w.write(author.toExternalString());
        w.flush();
        os.write('\n');

        os.write(P_COMMITTER);
        w.write(committer.toExternalString());
        w.flush();
        os.write('\n');

        if (!_UTF_8_.equals(encoding.name()))
        {//不是UTF-8的，要求写入
            os.write(P_ENCODING);
            os.write(Bytes.toBytesASCII(encoding.name()));
            os.write('\n');
        }

        os.write('\n');
        w.write(getMessage());
        w.flush();
        
        return os.toByteArray();
    }
    
    /******************************************************************************/
    //toString
    /******************************************************************************/
    
    public String toString()
    {
        return "提交[" + ObjectId.toString(tree) + " " + author + "]";
    }
}
